"""
    description: USB to CAN 自定义协议
                --包括 打包要发送的数据
                --包括 解码接收到的数据

    基于固件协议设计，采用整洁架构模式
"""

import struct
from enum import Enum
from typing import List, Optional, Tuple
from dataclasses import dataclass


# class CANBusState(Enum):
#     """CAN总线状态枚举"""
#     NORMAL = 0x00   # 总线正常
#     WARN = 0x01     # 总线警告
#     PASSIVE = 0x02  # 总线被动错误
#     OFFLINE = 0x03  # 设备离线

class BitRate(Enum):
    """波特率枚举"""
    CAN_BITRATE_10K   = 0x09
    CAN_BITRATE_20K   = 0x08
    CAN_BITRATE_25K   = 0x07
    CAN_BITRATE_50K   = 0x06
    CAN_BITRATE_100K  = 0x05
    CAN_BITRATE_125K  = 0x04
    CAN_BITRATE_250K  = 0x03
    CAN_BITRATE_500K  = 0x02
    CAN_BITRATE_750K  = 0x01
    CAN_BITRATE_1000K = 0x00
    

class UsbCanRWCode(Enum):
    """功能码枚举"""
    WRITE = 0x01      # 写操作
    READ = 0x00       # 读操作


@dataclass
class CANFrame:
    """CAN帧数据类 - 对应固件中的CUSTCAN_USBFrameTypeDef"""
    can_id     : int                    # CAN ID (32位)
    data       : List[int]               # 数据数组，最大8字节
    dlc        : int                      # 数据长度
    frame_info : int = 0           # 帧信息
    is_extended: bool = True      # 是否扩展帧
    is_remote  : bool = False       # 是否远程帧
    channel    : int = 0              # 通道号 (0-3)
    
         
    
    def __post_init__(self):
        """初始化后验证数据"""
        if len(self.data) > 8:
            raise ValueError("CAN数据长度不能超过8字节")
        if self.dlc > 8:
            raise ValueError("DLC不能超过8")
        # 补齐数据到8字节
        while len(self.data) < 8:
            self.data.append(0)
            
@dataclass
class UsbCanErrorFrame:
    """错误帧数据类 - 对应固件中的CUSTCAN_ErrorTypeDef"""
    error_code_list: list[int]              # 错误码
    header         : int = 0xEE          # 帧头
    tail           : int = 0xFF       # 帧尾
    
    def __post_init__(self):
        """确保数据长度为6字节"""
        if len(self.error_code_list) > 6:
            self.error_code_list = self.error_code_list[:6]
        while len(self.error_code_list) < 6:
            self.error_code_list.append(0)


# @dataclass
# class HeartBeatFrame:
#     """心跳帧数据类 - 对应固件中的CUSTCAN_HeartBeatFrameTypeDef"""
#     hearts_id: int = 0                      # 心跳ID
#     can0_send_rate: int = 0                 # CAN0发送速率
#     can0_recv_rate: int = 0                 # CAN0接收速率
#     device_state: DeviceStatus = None       # 设备状态
#     can0_send_err_cnt: int = 0              # CAN0发送错误计数
#     can0_recv_err_cnt: int = 0              # CAN0接收错误计数
#     usb_recv_err_cnt: int = 0               # USB接收错误计数
#     usb_send_err_cnt: int = 0               # USB发送错误计数
#     can0_recvbuff_overflow_cnt: int = 0     # 接收缓冲区溢出计数
    
#     def __post_init__(self):
#         if self.device_state is None:
#             self.device_state = DeviceStatus()

class UsbCanParamFuncCode(Enum):
    BitRate = 0x02
    

@dataclass
class UsbCanParamFrame:
    """参数帧数据类 - 对应固件中的CUSTCAN_ParamTypeDef,固定12字节"""
    func_code : int              # 功能码
    read_write: int             # 读写标志
    data      : List[int]             # 参数数据，固定8字节
    header    : int = 0xAA         #帧头
    tail      : int = 0xBB           #帧尾
    
    def __post_init__(self):
        """确保数据长度为8字节"""
        if len(self.data) > 8:
            self.data = self.data[:8]
        while len(self.data) < 8:
            self.data.append(0)

@dataclass
class UsbCanErrorFrame:
    """错误帧数据类 - 对应固件中的CUSTCAN_ErrorTypeDef"""
    error_code : int              # 错误码
    error_msg  : str              # 错误信息
    header     : int = 0xEE       # 帧头
    tail       : int = 0xFF       # 帧尾

class UsbCanProtocol:
    """USB CAN协议处理类 - 采用策略模式处理不同类型的帧"""
    
    # 协议常量
    CAN_FRAME_HEADER   = 0x5A
    CAN_FRAME_TAIL     = 0xA5
    CAN_FRAME_LEN      = 16
    PARAM_FRAME_HEADER = 0xAA  #
    PARAM_FRAME_TAIL   = 0xBB    #
    PARAM_FRAME_LEN    = 12
    ERROR_FRAME_HEADER = 0xEE  #
    ERROR_FRAME_TAIL   = 0xFF    #
    ERROR_FRAME_LEN    = 8
    
    
    def __init__(self):
        self.name = "USBCANProtocol"
        # self._frame_handlers = {
        #     FunctionCode.HEARTBEAT: self._handle_heartbeat_frame,
        # }
        
    def _msg(self, msg)->str:
        return f"[{self.name}] " + msg
    
    def unpack_can_frame_to_data(self, can_frame: CANFrame) -> list[int]:
        """
        打包CAN帧数据
        格式: 帧头(1) +  帧信息(1) + CAN_ID(4) + DLC(1) + 数据(8) + 帧尾(1) = 16字节
        """
        # 构建帧信息字节
        frame_info = 0
        frame_info |= (1 if can_frame.is_remote else 0) << 1
        frame_info |= (1 if not can_frame.is_extended else 0) << 2  # 注意：0=扩展帧
        frame_info |= (can_frame.channel & 0x03) << 3
        
        # 将32位CAN ID分解为4个字节 (大端序)
        can_id_bytes = struct.pack('>I', can_frame.can_id)
        
        # 打包整个帧
        frame_data = struct.pack('BB', 
                                self.CAN_FRAME_HEADER,
                                frame_info
                                )
        frame_data += can_id_bytes
        frame_data += struct.pack('B', can_frame.dlc)
        frame_data += struct.pack('8B', *can_frame.data)
        frame_data += struct.pack('B', self.CAN_FRAME_TAIL)
        
        return list(frame_data)
    
    def unpack_param_frame_to_data(self, param: UsbCanParamFrame) -> list[int]:
        """
        打包参数帧
        格式: 帧头(1) + 功能码(1) + 读写(1) + 数据(8) + 帧尾(1) = 12字节
        """
        # 数据验证：确保所有值都在字节范围内 (0-255)
        if param.func_code < 0 or param.func_code > 255:
            raise ValueError(f"func_code必须在0-255范围内，当前值: {param.func_code}")
        
        if param.read_write < 0 or param.read_write > 255:
            raise ValueError(f"read_write必须在0-255范围内，当前值: {param.read_write}")
        
        # 确保数据数组中的每个值都在字节范围内
        for i, val in enumerate(param.data):
            if val < 0 or val > 255:
                raise ValueError(f"data[{i}]必须在0-255范围内，当前值: {val}")
        
        # 修正：使用BBB8BB格式，确保帧头和帧尾也是单字节
        frame_data = struct.pack('>BBB8BB',
                                param.header,        # 单字节帧头 0xAA
                                param.func_code,     # 单字节功能码
                                param.read_write,    # 单字节读写标志
                                *param.data,         # 8个单字节数据
                                param.tail)          # 单字节帧尾 0xBB
        return list(frame_data)
    
    
    def pack_recv_data_to_frame(self, data: list[int]) -> Tuple[bool, List, str]:
        """
        解包CAN帧数据
        返回CANFrame对象或None（如果解包失败）
        """
        try:
            data_msg = ' '.join([f'{byte:02X}' for byte in data])
            
            if len(data) < 8:
                
                print(f"data length error, data: " + data_msg)
                return False, None, self._msg(f"data length error,data=[{data_msg}]")
            
            frame_list = []
            while len(data) > 0:
                if data[0] == self.PARAM_FRAME_HEADER and len(data) >= 12:
                    if data[11] != self.PARAM_FRAME_TAIL:
                        return False, None, self._msg(f"data tail is error; data=[{data_msg}]")
                    suc, frame, msg = self.pack_list_to_param_frame(data)
                    if suc:
                        frame_list.append(frame)
                        data = data[12:]
                        frame_list.append(frame)
                    # else:
                    #     return False, None, msg
                    
                elif data[0] == self.CAN_FRAME_HEADER and len(data) >= 16:
                    if data[15] != self.CAN_FRAME_TAIL:
                        return False, None, self._msg(f"data tail is error; data=[{data_msg}]")
                    suc, frame, msg = self.pack_data_to_can_frame(data)
                    if suc:
                        frame_list.append(frame)
                        data = data[16:]
                    # else:
                    #     return False, None, msg
                elif data[0] == self.ERROR_FRAME_HEADER and len(data) >= 8:
                    if data[7] != self.ERROR_FRAME_TAIL:
                        return False, None, self._msg(f"data tail is error; data=[{data_msg}]")
                    suc, frame, msg = self.pack_data_to_error_frame(data)
                    if suc:
                        frame_list.append(frame)
                        data = data[8:]
                    # else:
                    #     return False, None, msg
                else:
                    return False, None, self._msg(f"data header is error; data=[{data_msg}]")
            return True, frame_list, "ok"
        except Exception as e:
            return False, None, self._msg(f"pack_recv_to_frame error;data=[{data_msg}], {str(e)}")
    
    
    def pack_list_to_param_frame(self, data: list[int]) -> Tuple[bool, Optional[UsbCanParamFrame], str]:
        """打包参数帧对象"""
        if len(data) != 12:
            return False, None, self._msg(f"data lenth != 12, data=[{' '.join([f'{byte:02X}' for byte in data])}]")
         
        header = data[0]
        if header != self.PARAM_FRAME_HEADER:
            return False, None, self._msg(f"data header is error; data=[{' '.join([f'{byte:02X}' for byte in data])}]")
            
        tail = data[-1]
        if tail != self.PARAM_FRAME_TAIL:
            return False, None, self._msg(f"data tail is error; data=[{' '.join([f'{byte:02X}' for byte in data])}]")
            
        func_code = data[1]
        read_write = data[2]
        data_list = data[3:11]
        
        return True, UsbCanParamFrame(
            func_code=func_code,
            read_write=read_write,
            data=data_list
        ), self._msg("pack ok.")
        
    def pack_data_to_can_frame(self, data: list[int]) -> Tuple[bool, Optional[CANFrame], str]:
        """打包CAN帧对象"""
        header = data[0]
        if header != self.CAN_FRAME_HEADER:
            return False, None, self._msg(f"pack to can frame error, header error, data=[{data}]")
        tail = data[15]
        if tail != self.CAN_FRAME_TAIL:
            return False, None, self._msg(f"pack to can frame error, tail error, data=[{data}]")
        
        # 解析帧信息
        can_id = (data[2] << 24) | (data[3] << 16) | (data[4] << 8) | data[5]
        dlc = data[6]
        frame_info = data[1]
        can_data = data[7:15]   
        is_remote = bool((data[1] >> 1) & 0x01)
        is_extended = not bool((data[1] >> 2) & 0x01)  # 注意取反
        channel = (data[1] >> 3) & 0x03
        
        return True, CANFrame(
            can_id=can_id,
            data=can_data[:dlc],  # 只取有效数据长度
            dlc=dlc,
            frame_info=frame_info,
            is_extended=is_extended,
            is_remote=is_remote,
            channel=channel
        ), self._msg("pack ok.")
        
    def pack_data_to_error_frame(self, data: list[int]) -> Tuple[bool, Optional[UsbCanErrorFrame], str]:
        """打包错误帧对象"""
        header = data[0]
        if header != self.ERROR_FRAME_HEADER:
            return False, None, self._msg(f"pack to error frame error, header error, data=[{data}]")
        tail = data[-1]
        if tail != self.ERROR_FRAME_TAIL:
            return False, None, self._msg(f"pack to error frame error, tail error, data=[{data}]")
        
        error_code_list = data[1:7]
        return True, UsbCanErrorFrame(
            error_code_list=error_code_list
        ), self._msg("pack ok.")
        
        
        
            
    # def _handle_heartbeat_frame(self, data: bytes) -> Optional[HeartBeatFrame]:
    #     """处理心跳帧的策略方法"""
    #     return self.unpack_heartbeat_frame(data)
    
    # def auto_unpack(self, data: bytes) -> Tuple[bool, Optional[object]]:
    #     """
    #     自动识别并解包数据帧
    #     根据帧长度和帧头自动选择解包方法
    #     """
    #     if len(data) == 16 and data[0] == self.CAN_FRAME_HEADER:
    #         # 可能是CAN帧或心跳帧的一部分
    #         if len(data) >= 2 and data[1] == RWCode.HEARTBEAT.value:
    #             return False, None  # 心跳帧长度应该是18字节
    #         return self.unpack_can_frame(data)
    #     elif len(data) == 18 and data[0] == self.CAN_FRAME_HEADER:
    #         return self.unpack_heartbeat_frame(data)
    #     elif len(data) == 22:
    #         return self.unpack_param_frame(data)
        
    #     return False, None


# 工厂模式创建不同类型的帧
class FrameFactory:
    """帧工厂类 - 使用工厂模式创建不同类型的帧对象"""
    
    @staticmethod
    def create_can_frame(can_id: int, data: List[int], 
                        is_extended: bool = True, 
                        channel: int = 0 ) -> CANFrame:
        """创建CAN帧"""
        return CANFrame(
            can_id=can_id,
            data=data.copy(),
            dlc=len(data),
            is_extended=is_extended,
            channel=channel
        )
    
    # @staticmethod
    # def create_heartbeat_frame() -> HeartBeatFrame:
    #     """创建心跳帧"""
    #     return HeartBeatFrame()
    
    @staticmethod
    def create_param_frame(func_code: int, is_write: bool, 
                          data: List[int]) -> UsbCanParamFrame:
        """创建参数帧"""
        return UsbCanParamFrame(
            func_code=func_code,
            read_write=UsbCanRWCode.WRITE.value if is_write else UsbCanRWCode.READ.value,
            data=data.copy()
        )


# 保持向后兼容的类名
class UsbCANFrame(CANFrame):
    """向后兼容的UsbCan类，继承自CANFrame"""
    def __init__(self, name: str = "default"):
        super().__init__(can_id=0, data=[0]*8, dlc=8)
        self.name = name
        # 兼容原有属性
        self.header = self.CAN_FRAME_HEADER
        self.tail = self.CAN_FRAME_TAIL


class RecvQueue:
    """
    CAN接收队列类 - 用于管理接收到的list[str]数据
    支持添加、取出、删除和查看所有数据的功能
    """
    
    def __init__(self, max_size: int = 1000):
        """
        初始化循环队列
        
        Args:
            max_size: 队列最大容量，默认1000
        """
        self.name = "RecvQueue"
        self._queue = []  # 存储数据的列表
        self._max_size = max_size
        self._current_position = 0  # 当前位置指针
        self._recv_index = 0 #接收计数
        
    def _msg(self, msg) -> str:
        return f"[{self.name}]" + msg
    
    def put(self, data: Optional[CANFrame | UsbCanParamFrame] ) -> Tuple[bool, int, str]:
        """
        将CANFrame数据添加到队列中
        
        Args:
            data: 要添加的CANFrame数据
            
        Returns:
            Tuple[bool, int, str]: (是否成功, 数据在队列中的位置, 错误信息)
        """
        if not isinstance(data, CANFrame | UsbCanParamFrame | UsbCanErrorFrame):
            return False, None, self._msg("数据必须是CANFrame或ParamFrame或UsbCanErrorFrame类型")
        
        # 如果队列已满，移除最旧的数据（循环覆盖）
        if len(self._queue) >= self._max_size:
            # self._queue.pop(0)  # 移除最旧的数据
            return False, None, self._msg("队列已满")
        
        # 添加新数据
        self._queue.append(data)
        position = len(self._queue) - 1  # 返回在当前队列中的位置
        
        # print(f"✓ 数据已添加到队列位置 {position}: {data}")
        return True, position, self._msg("数据已添加到队列位置")
    
    def pop(self) -> Optional[CANFrame | UsbCanParamFrame | UsbCanErrorFrame]:
        """
        从队列中取出CANFrame数据（FIFO - 先进先出）
        
        Returns:
            CANFrame: 取出的数据
        """
        if len(self._queue) == 0:
            return None
        
        data = self._queue.pop(0)  # 取出第一个数据
        
        # print(f"✓ 从队列位置 0 取出数据: {data}")
        return data
    
    def pop_last(self) -> Optional[CANFrame | UsbCanParamFrame | UsbCanErrorFrame]:
        """
        取出最新的一个数据（LIFO - 后进先出）
        
        Returns:
            CANFrame: 取出的最新数据，如果队列为空返回None
        """
        if len(self._queue) == 0:
            return None
        
        data = self._queue.pop()  # 取出最后一个数据（最新的）
        print(f"✓ 从队列末尾取出最新数据: {data}")
        return data
    
    def get_last(self) -> Optional[CANFrame | UsbCanParamFrame | UsbCanErrorFrame]:
        """
        获取最新的一个数据但不移除
        
        Returns:
            CANFrame: 最新的数据，如果队列为空返回None
        """
        if len(self._queue) == 0:
            return None
        
        data = self._queue[-1]  # 获取最后一个数据（最新的）但不移除
        print(f"✓ 获取最新数据: {data}")
        return data
    
    def remove(self, del_data: Optional[CANFrame | UsbCanParamFrame | UsbCanErrorFrame]) -> Tuple[bool, str]:
        """
        根据CANFrame对象相等性删除匹配的数据
        
        Args:
            del_data: 要删除的CANFrame对象
            
        Returns:
            Tuple[bool, str]: 删除成功返回True和消息
        """
        
        if len(self._queue) == 0:
            return False, "队列为空，无法删除数据"
        
        if not isinstance(del_data, Optional[CANFrame | UsbCanParamFrame | UsbCanErrorFrame]):
            return False, "删除数据必须是CANFrame类型"
        
        # 查找相等的对象
        for i, data in enumerate(self._queue):
            if data == del_data:  # 直接使用对象相等性比较
                # 找到相等的对象，删除它
                removed_data = self._queue.pop(i)
                print(f"✓ 已删除位置 {i} 的数据: {removed_data}")
                return True, f"成功删除匹配的数据，原位置: {i}"
        
        # 没有找到相等的对象
        return False, f"未找到相等的数据对象"

        
    def get_all_data(self) -> list[Optional[CANFrame| UsbCanParamFrame | UsbCanErrorFrame]]:
        """
        返回队列中所有数据
        """
        return self._queue
    
    def size(self) -> int:
        """返回队列当前大小"""
        return len(self._queue)
    
    def empty(self) -> bool:
        """检查队列是否为空"""
        return len(self._queue) == 0
    
    def is_full(self) -> bool:
        """检查队列是否已满"""
        return len(self._queue) >= self._max_size
    
    def clear(self):
        """清空队列"""
        self._queue.clear()
        print("✓ 队列已清空")
    
    def __str__(self) -> str:
        """字符串表示"""
        if not self._queue:
            return "CircularBytesQueue: 空队列"
        
        result = f"CircularBytesQueue: {len(self._queue)}/{self._max_size} 项\n"
        for i, data in enumerate(self._queue):
            result += f"  [{i}]: {data}\n"
        return result.rstrip()
    
    def __repr__(self) -> str:
        return f"CircularBytesQueue(size={len(self._queue)}, max_size={self._max_size})"


# 主协议类的别名，保持向后兼容
usbcan_protocol = UsbCanProtocol
    